#include "wx/wxprec.h"
#if wxUSE_STREAMS

#include "wx/sstream.h"
#include "wx/hashmap.h"

wxStringInputStream::wxStringInputStream( const wxString& s )
  : m_str( s ), m_buf( wxMBConvUTF8().cWX2MB( s ).release() ), m_len( strlen( m_buf ) ) {
  wxASSERT_MSG( m_buf != NULL, _T( "Could not convert string to UTF8!" ) );
  m_pos = 0;
}

wxStringInputStream::~wxStringInputStream() {
  free( m_buf );
}

wxFileOffset wxStringInputStream::GetLength() const {
  return m_len;
}


wxFileOffset wxStringInputStream::OnSysSeek( wxFileOffset ofs, wxSeekMode mode ) {
  switch( mode ) {
    case wxFromStart:
      // nothing to do, ofs already ok
      break;
    case wxFromEnd:
      ofs += m_len;
      break;
    case wxFromCurrent:
      ofs += m_pos;
      break;
    default:
      wxFAIL_MSG( _T( "invalid seek mode" ) );
      return wxInvalidOffset;
  }
  if( ofs < 0 || ofs > wx_static_cast( wxFileOffset, m_len ) ) {
    return wxInvalidOffset;
  }
  m_pos = wx_truncate_cast( size_t, ofs );
  return ofs;
}

wxFileOffset wxStringInputStream::OnSysTell() const {
  return wx_static_cast( wxFileOffset, m_pos );
}

size_t wxStringInputStream::OnSysRead( void *buffer, size_t size ) {
  const size_t sizeMax = m_len - m_pos;
  if( size >= sizeMax ) {
    if( sizeMax == 0 ) {
      m_lasterror = wxSTREAM_EOF;
      return 0;
    }
    size = sizeMax;
  }
  memcpy( buffer, m_buf + m_pos, size );
  m_pos += size;
  return size;
}

wxFileOffset wxStringOutputStream::OnSysTell() const {
  return wx_static_cast( wxFileOffset, m_pos );
}

WX_DECLARE_VOIDPTR_HASH_MAP( wxMemoryBuffer, wxStringStreamUnconvBuffers );

static wxStringStreamUnconvBuffers gs_unconverted;

wxStringOutputStream::~wxStringOutputStream() {
  gs_unconverted.erase( this );
}

size_t wxStringOutputStream::OnSysWrite( const void *buffer, size_t size ) {
  const char *p = wx_static_cast( const char *, buffer );
  const char *src;
  size_t srcLen;
  wxMemoryBuffer& unconv = gs_unconverted[this];
  if( unconv.GetDataLen() ) {
    unconv.AppendData( p, size );
    src = unconv;
    srcLen = unconv.GetDataLen();
  } else { // no unconverted data left, avoid extra copy
    src = p;
    srcLen = size;
  }
  wxWCharBuffer wbuf( m_conv.cMB2WC( src, srcLen, NULL /* out len */ ) );
  if( wbuf ) {
    unconv = wxMemoryBuffer( 0 );
    *m_str += wbuf;
  } else { // conversion failed
    if( src == p ) {
      unconv.AppendData( src, srcLen );
    }
    return size;
  }
  m_pos += size;
  return size;
}

#endif
